package com.netqin.netconnection;

import java.util.Vector;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import com.netqin.cache.DataCache;
import com.zcl.utils.NqLog;
import com.zcl.utils.NqUtil;
import com.zcl.utils.Value;

/**
 * 
 * <h2>HTTP的多任务下载实现</h2> <h4>注意：
 * <ol>
 * <li>需要在包含MessageQueue的线程（比如主线程）中调用（需要和通过MessageQueue和调用线程通信)
 * <li>需要android.permission.INTERNET能力
 * </ol>
 * </h4> <h3>实现细节</h3>
 * <ol>
 * <li>每次新加入任务或完成一个任务后，都会新启一个线程负责下载（仅支持单线程下载）
 * <ol>
 * <li>新添任务首先检查当前缓存，如果有的话，直接通知Handler对应线程，否则，进入下载流程；
 * <li>进入下载流程:插入到任务队列（需要检查是否已有同样的任务,确定更新还是添加），如果当前任务队列处于空闲状态，则进入下载；
 * </ol>
 * <li>队列可暂停；停止；继续
 * <ol>
 * <li>暂停或停止时，对已经在下载状态的任务不产生影响，仅影响后续任务是否进入下载；
 * <li>其中暂停后，队列可以继续，不清空当前下载队列；
 * <li>停止时，将清空当前下载队列，但是已经在下载状态的任务维持现有状态；
 * </ol>
 * </ol>
 * 
 * @author MingLi
 * @since 2011-11-10
 */

public class FileDownloadMgrImp implements FileDownloadMgr {

    private static final String TAG = "FileDownloadMgrImp";

    public FileDownloadMgrImp(Context context) {
        mContext = context;
    }

    /**
     * @param progress
     *            是否发送下载进度，目前只有大图才会发送
     */
    @Override
    public FileDownloadTask addFileDownloadTask(String url, Message msg, Handler handler, boolean progress) {
        return addFileDownloadTask(url, msg, handler, progress, progress);
    }

    @Override
    public FileDownloadTask addFileDownloadTask(String url, Message msg, Handler handler) {
        return addFileDownloadTask(url, msg, handler, false);
    }

    /**
     * 将任务添加到队列的头部，用于优先下载
     * 
     * @param url
     * @param msg
     * @param handler
     * @param progress 当为true时，将启动大图缓存
     * @param front
     *            添加到队列的头部，提供优先下载
     * @return
     */
    public FileDownloadTask addFileDownloadTask(String url, Message msg, Handler handler, boolean progress, boolean front) {
        // 将任务添加到队列中，不检查是否已经下载成功与否
        FileDownloadTaskImp d = null;
        if (TextUtils.isEmpty(url)) {
            return d;
        }
        NDTask e = new NDTask();
        e.handler = handler;
        e.url = url;
        e.msg = msg;
        e.progress = progress;
        e.state = TaskState.kIdle;
        d = new FileDownloadTaskImp(e);
        if (front) {
            mTasksList.insertElementAt(d, 0);
        } else {
            mTasksList.add(d);
        }
        if (Value.printLog) {
            NqLog.d(TAG, "add url::" + url);
        }
        start();
        return d;
    }

    @Override
    public void start() {
        // 确保队列中的线程处于启动状态
        if (!mThread.isAlive()) {
            mThread.start();
        }
        // 线程启动了，但是目前处于停止状态
        if (isStopped()) {
            mStop = false;
            synchronized (mRunnable) {
                mRunnable.notify();
            }
        }
    }

    @Override
    public void stop() {
        // 设置队列的状态为停止，下载线程将在下次任务时停止
        mStop = true;
    }

    @Override
    public void pause() {
        stop();
    }

    @Override
    public void resume() {
        // 和stop做相反的操作，并调用start确保线程启动
        start();
    }

    @Override
    public void start(FileDownloadTask t) {
        // 将一个任务的状态设置为待下载状态
        NDTask task = getNDTask(t);
        synchronized (mTasksList) {
            if (task != null && task.state != TaskState.kIdle) {
                task.state = TaskState.kIdle;
            }
        }
        start();
    }

    /**
     * 获取任务在队列中的位置
     * 
     * @return -1表示未找到
     */
    private synchronized int indexOfTask(FileDownloadTask t) {
        if (t == null || mTasksList == null || mTasksList.size() == 0) {
            return -1;
        }
        return mTasksList.indexOf(t);
    }

    /*
     * 获取对应的内部任务状态，可能为null
     */
    private NDTask getNDTask(FileDownloadTask t) {
        synchronized (mTasksList) {
            int index = indexOfTask(t);
            if (index != -1 && mTasksList.get(index) instanceof FileDownloadTaskImp) {
                FileDownloadTaskImp task = (FileDownloadTaskImp) mTasksList.get(index);
                return task.getTask();
            }
        }
        return null;
    }

    private void remove(FileDownloadTask t) {
        stop(t);
    }

    @Override
    public void stop(FileDownloadTask t) {
        // 移除一个任务，如果任务处于下载状态，将不能移除
        NDTask task = getNDTask(t);
        if (task != null && task.state != TaskState.kRunning) {
            mTasksList.remove(indexOfTask(t));
        }
    }

    @Override
    public void pause(FileDownloadTask t) {
        NDTask task = getNDTask(t);
        synchronized (mTasksList) {
            if (task != null && task.state != TaskState.kRunning) {
                task.state = TaskState.kPaused;
            }
        }
    }

    @Override
    public void resume(FileDownloadTask t) {
        // 将任务的状态设置为待下载状态，并确保队列启动
        NDTask task = getNDTask(t);
        synchronized (mTasksList) {
            if (task != null && task.state != TaskState.kPaused) {
                task.state = TaskState.kIdle;
            }
        }
        start();
    }

    enum TaskState {
        kIdle, kRunning, kPaused, kSucceeded, kFailed
    }

    // 单个任务的描述类
    private class NDTask {
        public String url;
        public Message msg;
        public Handler handler;
        public boolean progress;// 发送下载进度
        public TaskState state;

        public boolean equals(Object comp) {
            if (comp == null || !(comp instanceof NDTask)) {
                return false;
            }

            NDTask task = (NDTask) comp;
            return task.handler == handler && TextUtils.equals(task.url, url);
        }
    }

    private class FileDownloadTaskImp implements FileDownloadTask {
        NDTask obj;

        public boolean equals(Object comp) {
            if (comp == null || !(comp instanceof FileDownloadTaskImp)) {
                return false;
            }

            FileDownloadTaskImp d = (FileDownloadTaskImp) comp;
            return d.obj.equals(obj);
        }

        FileDownloadTaskImp(NDTask e) {
            obj = e;
        }

        /**
         * 
         * 获取当前任务的下载状态
         */
        public TaskStatusCode getStatus() {
            FileDownloadTask.TaskStatusCode r = FileDownloadTask.TaskStatusCode.Failed;
            DataCache cache = getDataCache();
            if (getTask() != null && getTask().progress) {
                cache = DataCache.getLargeImagesInstance(mContext);
            }

            // 不要从队列中匹配，因为可能已经被移除了
            if (null != cache) {
                if (cache.isAvailable(getUrl())) {
                    r = TaskStatusCode.Succeeded;
                }
            }
            return r;
        }

        public Message getMessage() {
            Message msg = null;

            NDTask task = getTask();
            if (null == task || null == task.msg) {
                return msg;
            }
            msg = task.handler.obtainMessage();
            msg.copyFrom(task.msg);
            return msg;
        }

        public String getFilePath() {
            String strPath = null;

            NDTask task = getTask();
            if (null != task) {
                DataCache d = getDataCache();
                if (task.progress) {
                    d = DataCache.getLargeImagesInstance(mContext);
                }

                strPath = d.getData(obj.url);
            }

            return strPath;
        }

        public String getUrl() {
            String strUrl = null;

            NDTask task = getTask();
            if (null != task) {
                strUrl = task.url;
            }
            return strUrl;
        }

        // 内部实现用
        public NDTask getTask() {
            return obj;
        }
    }

    /**
     * 获取与下载任务相关的缓存
     * 
     * @return 如果没有设置，将创建一个缓存，始终不为空
     */
    public DataCache getDataCache() {
        if (mDataCache == null) {
            mDataCache = DataCache.getInstance(mContext);
        }
        return mDataCache;
    }

    /**
     * 设置下载使用的缓存，每次下载前将检查缓存，确定是否需要下载
     * 
     * @param dataCache
     */
    public void setDataCache(DataCache dataCache) {
        mDataCache = dataCache;
    }

    /**
     * 队列是否处于停止状态
     * 
     * @return
     */
    public boolean isStopped() {
        return mStop;
    }

    /**
     * 获取下一个待处理的任务.仅读取，不做删除处理.如果删除，需要调用remove。
     */
    private synchronized FileDownloadTask getNextTask() {
        if (mTasksList != null && mTasksList.size() > 0) {
            FileDownloadTaskImp task;
            for (int i = 0; i < mTasksList.size(); i++) {
                if (mTasksList.get(i) instanceof FileDownloadTaskImp) {
                    task = (FileDownloadTaskImp) mTasksList.get(i);
                    if (task.getTask() instanceof NDTask) {
                        if (task.getTask().state == TaskState.kIdle) {
                            return task;
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * 负责具体发送工作的代码
     */
    Runnable mRunnable = new Runnable() {
        @Override
        public void run() {
            while (true) {
                // 1.获取下一个任务
                FileDownloadTaskImp taskImp = (FileDownloadTaskImp) getNextTask();
                if (taskImp != null && !isStopped()) {
                    DataCache dataCache = getDataCache();
                    NDTask task = taskImp.getTask();
                    if (task != null && task.progress) {
                        dataCache = DataCache.getLargeImagesInstance(mContext);
                    }

                    // 2.检查缓存是否包含该任务的结果
                    if (null != dataCache && dataCache.isAvailable(task.url) && NqUtil.fileExists(dataCache.getData(task.url))) {
                        if (Value.printLog) {
                            NqLog.d(TAG, "downloaded url::" + task.url);
                        }
                        if (task.handler != null) {
                            Message msgToCaller = task.handler.obtainMessage();
                            msgToCaller.copyFrom(task.msg);
                            msgToCaller.obj = taskImp;
                            task.handler.sendMessage(msgToCaller);
                        }
                        remove(taskImp);
                        continue;
                    }
                    // 3.发起网络请求
                    task.state = TaskState.kRunning;
                    // 下载成功，设置状态，发送通知，移除任务
                    // 失败：设置缓存中的状态，发送通知，移除任务
                    try {
                        Connection http = HttpConnection.getInstance(mContext, dataCache);// 下载接口
                        NqLog.d(TAG, "start downloading url::" + task.url + " datacache::" + dataCache);

                        if (!TextUtils.isEmpty(task.url) && null != http) {
                            {
                                http.setHandler(task.handler);
                                http.progress(task.progress);
                                if (TextUtils.isEmpty(http.addUrl(task.url).get(dataCache))) {
                                    // fail
                                    task.state = TaskState.kFailed;
                                    if (dataCache != null) {// 目前仅针对大图提供重新下载功能
                                        if (task.progress) {
                                            dataCache.deleteData(task.url);
                                            NqLog.d(TAG, "remove error download::" + task.url);
                                        } else {
                                            NqLog.d(TAG, "set download type false::" + task.url);
                                            dataCache.addDownLoadType(task.url, false);
                                        }
                                    }
                                } else {
                                    // succeed
                                    task.state = TaskState.kSucceeded;
                                }
                                NqLog.d(TAG, "download result:" + task.state);
                                if (task.handler != null) {
                                    Message msgToCaller = task.handler.obtainMessage();
                                    msgToCaller.copyFrom(task.msg);
                                    msgToCaller.obj = taskImp;
                                    task.handler.sendMessage(msgToCaller);
                                }
                                remove(taskImp);
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else {
                    synchronized (this) {
                        // idle
                        try {
                            if (Value.printLog) {
                                NqLog.d(TAG, "waiting for next task");
                            }
                            mStop = true;
                            wait();
                            if (Value.printLog) {
                                NqLog.d(TAG, "keep on going for next task");
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    };

    Vector<Object> mTasksList = new Vector<Object>();
    DataCache mDataCache;
    Thread mThread = new Thread(mRunnable);
    boolean mStop;// 停止发送队列
    Context mContext;// note::application context instead activity context to
                     // prevent memory leak
}